<template>
  <view class="uqrcode" :style="{ width: `${size}px`, height: `${size}px` }">
    <block v-if="!isReload">
      <!-- canvas模式，默认 -->
      <block v-if="mode === 'canvas'">
        <!-- #ifdef APP-NVUE -->
        <gcanvas
          class="uqrcode-canvas"
          ref="gcanvas"
          :style="{ width: `${size}px`, height: `${size}px` }"
        ></gcanvas>
        <!-- #endif -->
        <!-- #ifndef APP-NVUE -->
        <canvas
          class="uqrcode-canvas"
          :id="id"
          :canvas-id="id"
          :style="{ width: `${size}px`, height: `${size}px` }"
        ></canvas>
        <!-- #endif -->
      </block>

      <!-- view模式，兼容 -->
      <view
        v-else-if="mode === 'view'"
        class="uqrcode-view"
        :style="{
          width: `${size}px`,
          height: `${size}px`,
          padding: `${margin}px`,
          'background-color': backgroundColor,
        }"
      >
        <view
          class="uqrcode-view-row"
          v-for="(row, rowIndex) in modules.length"
          :key="rowIndex"
        >
          <view
            class="uqrcode-view-col"
            v-for="(col, colIndex) in modules.length"
            :key="colIndex"
            :style="{
              width: `${tileSize}px`,
              height: `${tileSize}px`,
              'background-color': modules[rowIndex][colIndex]
                ? foregroundColor
                : backgroundColor,
            }"
          >
          </view>
        </view>
      </view>
    </block>

    <!-- 生成二维码的loading效果 -->
    <!-- <view class="uqrcode-makeing" :class="{'uqrcode-make-complete': !makeing}">loading...</view> -->

    <!-- H5保存提示 -->
    <!-- #ifdef H5 -->
    <view class="uqrcode-h5-save" v-if="isH5Save">
      <image class="uqrcode-h5-save-image" :src="tempFilePath"></image>
      <text class="uqrcode-h5-save-text">请长按二维码进行保存</text>
      <view class="uqrcode-h5-save-close" @click="isH5Save = false">
        <view class="uqrcode-h5-save-close-before"></view>
        <view class="uqrcode-h5-save-close-after"></view>
      </view>
    </view>
    <!-- #endif -->
  </view>
</template>

<script>
/* 引入uqrcode核心js */
import uqrcode from "./uqrcode";

/* 引入nvue所需模块 */
// #ifdef APP-NVUE
import { enable, WeexBridge } from "./gcanvas";
let modal = weex.requireModule("modal");
// #endif

export default {
  name: "uqrcode",
  props: {
    // id
    id: {
      type: String,
      default: uuid(),
    },
    // 生成模式
    mode: {
      type: String,
      default: "canvas", // canvas|view (nvue不支持canvas模式)
    },
    // 二维码内容
    text: String | null | undefined,
    // 二维码大小
    size: {
      type: Number,
      default: 256,
    },
    // 填充边距
    margin: {
      type: Number,
      default: 0,
    },
    // 背景色
    backgroundColor: {
      type: String,
      default: "#FFFFFF",
    },
    // 前景色
    foregroundColor: {
      type: String,
      default: "#000000",
    },
    // 纠错等级
    errorCorrectLevel: {
      type: [String, Number],
      default: uqrcode.errorCorrectLevel.H,
    },
    // 版本
    typeNumber: {
      type: Number,
      default: -1,
    },
    // 导出的文件类型
    fileType: {
      type: String,
      default: "png",
    },
  },
  data() {
    return {
      canvasContext: null,
      makeing: false,
      delegate: null,
      delegateParams: null,
      tempFilePath: "",
      isH5Save: false,
      isReload: false,
    };
  },
  computed: {
    modules() {
      let options = {
        ...this.$props,
      };
      if (typeof options.errorCorrectLevel === "string") {
        options.errorCorrectLevel =
          uqrcode.errorCorrectLevel[options.errorCorrectLevel];
      }
      return uqrcode.getModules(options);
    },
    tileSize() {
      return (this.size - this.margin * 2) / this.modules.length;
    },
  },
  watch: {
    /* 深度监听props，任意属性一发生改变立即重绘二维码 */
    $props: {
      handler() {
        this.reload();
      },
      deep: true,
    },
    makeing(val) {
      if (!val) {
        if (typeof this.delegate === "function") {
          this.delegate(this.delegateParams);
        }
      }
    },
  },
  mounted() {
    this.$nextTick(() => {
      this.make();
    });
  },
  methods: {
    reload() {
      /* 重载组件 */
      this.isReloadMake = true;
      this.isReload = true;
      this.$nextTick(() => {
        this.isReload = false;
        this.$nextTick(() => {
          setTimeout(() => {
            this.make();
          }, 150);
        });
      });
    },
    make() {
      if (this.makeing) {
        return;
      }
      this.makeing = true;
      if (this.mode === "canvas") {
        let ctx = null;

        // #ifdef APP-NVUE
        /* 获取元素引用 */
        let gcanvas = this.$refs["gcanvas"];
        /* 通过元素引用获取canvas对象 */
        let canvasObj = enable(gcanvas, {
          bridge: WeexBridge,
        });
        /* 获取绘图所需的上下文，目前不支持3d */
        ctx = canvasObj.getContext("2d");
        // #endif

        // #ifndef APP-NVUE
        /* 获取绘图所需的上下文 */
        ctx = uni.createCanvasContext(this.id, this);
        // #endif

        this.canvasContext = ctx;

        ctx.draw(); // 清空之前的画布内容
        ctx.setFillStyle(this.backgroundColor);
        ctx.fillRect(0, 0, this.size, this.size);

        for (var row = 0; row < this.modules.length; row++) {
          for (var col = 0; col < this.modules.length; col++) {
            // 计算每一个小块的位置
            var x = col * this.tileSize + this.margin;
            var y = row * this.tileSize + this.margin;
            var w = this.tileSize;
            var h = this.tileSize;

            var style = this.modules[row][col]
              ? this.foregroundColor
              : this.backgroundColor;
            ctx.setFillStyle(style);
            ctx.fillRect(x, y, w, h);
          }
        }

        ctx.draw(false, () => {
          // setTimeout(() => {
          this.complete();
          // }, 3000)
        });
      } else if (this.mode === "view") {
        this.complete();
      }
    },
    complete(e = {}) {
      let basic = {
        id: this.id,
        text: this.text,
        mode: this.mode,
      };
      let ages = {
        ...basic,
        ...e,
      };
      this.makeing = false;
      this.$emit("complete", ages);
    },
    toTempFilePath(callback = {}) {
      if (typeof callback.success != "function") {
        callback.success = () => {};
      }
      if (typeof callback.fail != "function") {
        callback.fail = () => {};
      }
      if (typeof callback.complete != "function") {
        callback.complete = () => {};
      }

      if (this.makeing) {
        // 如果还在生成状态，那当前操作将托管到委托，监听生成完成后再通过委托复调当前方法
        this.delegate = this.toTempFilePath;
        this.delegateParams = callback;
        return;
      } else {
        this.delegate = null;
        this.delegateParams = null;
      }

      let _this = this;
      // #ifdef APP-NVUE
      this.canvasContext.toTempFilePath(
        0,
        0,
        _this.size * 3, // 不知道什么原因，最少要*3，不然输出的图片只有一个角
        _this.size * 3, // 不知道什么原因，最少要*3，不然输出的图片只有一个角
        _this.size,
        _this.size,
        _this.fileType,
        1,
        (res) => {
          _this.tempFilePath = res.tempFilePath;
          callback.success(res);
          callback.complete(res);
        }
      );
      // #endif

      // #ifndef APP-NVUE
      uni.canvasToTempFilePath(
        {
          canvasId: this.id,
          fileType: this.fileType,
          width: this.size,
          height: this.size,
          success: (res) => {
            this.tempFilePath = res.tempFilePath;
            callback.success(res);
          },
          fail: (err) => {
            callback.fail(err);
          },
          complete: (res) => {
            callback.complete(res);
          },
        },
        this
      );
      // #endif
    },
    save(callback = {}) {
      if (typeof callback.success != "function") {
        callback.success = () => {};
      }
      if (typeof callback.fail != "function") {
        callback.fail = () => {};
      }
      if (typeof callback.complete != "function") {
        callback.complete = () => {};
      }

      this.toTempFilePath({
        success: (res) => {
          // #ifdef H5
          this.isH5Save = true;
          callback.success({
            msg: "H5请长按图片保存",
          });
          callback.complete({
            msg: "H5请长按图片保存",
          });
          // #endif

          // #ifndef H5
          uni.saveImageToPhotosAlbum({
            filePath: this.tempFilePath,
            success: (res1) => {
              callback.success({
                msg: "保存成功",
              });
            },
            fail: (err1) => {
              callback.fail(err1);
            },
            complete: (res1) => {
              callback.complete(res1);
            },
          });
          // #endif
        },
        fail: (err) => {
          callback.fail(err);
        },
      });
    },
  },
};

function uuid(len = 32, firstU = true, radix = null) {
  let chars =
    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
  let uuid = [];
  radix = radix || chars.length;

  if (len) {
    // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
    for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
  } else {
    let r;
    // rfc4122标准要求返回的uuid中,某些位为固定的字符
    uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
    uuid[14] = "4";

    for (let i = 0; i < 36; i++) {
      if (!uuid[i]) {
        r = 0 | (Math.random() * 16);
        uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
      }
    }
  }

  // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
  if (firstU) {
    uuid.shift();
    return "u" + uuid.join("");
  } else {
    return uuid.join("");
  }
}
</script>

<style>
.uqrcode {
  position: relative;
}

.uqrcode-makeing {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  /* #ifndef APP-NVUE */
  display: flex;
  box-sizing: border-box;
  /* #endif */
  justify-content: center;
  align-items: center;
  background-color: #ffffff;
  transition-property: opacity;
  transition-duration: 0.25s;
  opacity: 0.88;
}

.uqrcode-make-complete {
  opacity: 0;
}

.uqrcode-view {
  /* #ifndef APP-NVUE */
  display: flex;
  box-sizing: border-box;
  /* #endif */
  flex-direction: column;
}

.uqrcode-view-row {
  /* #ifndef APP-NVUE */
  display: flex;
  box-sizing: border-box;
  /* #endif */
  flex-direction: row;
}

.uqrcode-view-col {
  /* #ifndef APP-NVUE */
  display: flex;
  box-sizing: border-box;
  /* #endif */
}

/* #ifdef H5 */
.uqrcode-h5-save {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 100;
  background-color: rgba(0, 0, 0, 0.68);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.uqrcode-h5-save-image {
  width: 512rpx;
  height: 512rpx;
  padding: 32rpx;
}

.uqrcode-h5-save-text {
  margin-top: 20rpx;
  font-size: 32rpx;
  font-weight: 700;
  color: #ffffff;
}

.uqrcode-h5-save-close {
  position: relative;
  margin-top: 72rpx;
  width: 40rpx;
  height: 40rpx;
  border: 2rpx solid #ffffff;
  border-radius: 40rpx;
  padding: 10rpx;
}

.uqrcode-h5-save-close-before {
  position: absolute;
  top: 50%;
  transform: translateY(-50%) rotate(45deg);
  width: 40rpx;
  height: 4rpx;
  background: #ffffff;
}

.uqrcode-h5-save-close-after {
  position: absolute;
  top: 50%;
  transform: translateY(-50%) rotate(-45deg);
  width: 40rpx;
  height: 4rpx;
  background: #ffffff;
}

/* #endif */
</style>
